#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/percpu.h>
#include <linux/sched.h>
#include <linux/sched/task.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/smpboot.h>
#include <linux/time.h>

/************************************************************************************
 *
 *          测试点
 *
 *	线程亲和度 	  绑核问题
 *	smp 	 	mb问题 rmb smb_rmb
 *	smp      	锁问题
 *	barrier  	优化屏障问题
 *	cpu      	位图问题
 *   per-cpu     每cpu变量测试
 *	cpu state	状态查询、设置问题
 *	smp         Cache多核同步问题
 *   单核         数据和指令Cache问题
 *************************************************************************************/

static DECLARE_COMPLETION(thread_done);
static atomic_long_t count = ATOMIC64_INIT(0);

static int thread_func(void *arg) {
  int cpu;
  long long val;

  cpu = *(int *)arg;
  pr_err("%s start on cpu%d\n", __func__, cpu);

  msleep(10000);
  pr_err("%s end on cpu%d\n", __func__, cpu);

  if ((val = atomic64_inc_return(&count)) >= nr_cpu_ids) {
    pr_err("%s %d end atomic %lld\n", __func__, cpu, val);
    complete(&thread_done);
  }

  return 0;
}

static void self_test(void) {
  unsigned int cpu, ret;
  struct task_struct *kthread[8] = {0};

  init_completion(&thread_done);
  memset(&count, 0, sizeof(count));

  for_each_online_cpu(cpu) {
    kthread[cpu] =
        kthread_create_on_cpu(thread_func, (void *)&cpu, cpu, "cjthread/%d");
    if (IS_ERR(kthread[cpu])) {
      pr_err("fail to kthread_create_on_cpu\n");
      return;
    }
  }

  pr_info("NR_CPUS: %d, nr_cpu_ids: %d\n", NR_CPUS, nr_cpu_ids);

  for_each_online_cpu(cpu) {
    ret = wake_up_process(kthread[cpu]);
    pr_err("wake_up_process %d ret %d\n", cpu, ret);
  }
  wait_for_completion(&thread_done);
  pr_err("%s %d completed\n", __func__, __LINE__);
}

static int __init test_smp_init(void) {
  pr_info("smp module init\n");

  self_test();

  return 0;
}

static void __exit test_smp_exit(void) { pr_info("smp module exit\n"); }

module_init(test_smp_init);
module_exit(test_smp_exit);

MODULE_DESCRIPTION("hello - Linux Lab module example");
MODULE_AUTHOR("jiangxiujie <one_face@sina.com>");
MODULE_LICENSE("GPL");
